package com.yuelao.yuelao_backend.common;

import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import java.util.HashMap;
import java.util.Map;

@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {

    /**
     * 业务异常处理
     * @param e 业务异常
     * @return ApiResponse
     */
    @ExceptionHandler(BizException.class)
    @ResponseStatus(HttpStatus.OK) // 业务异常返回200，通过响应体中的 code 区分
    public ApiResponse<?> bizExceptionHandler(BizException e, HttpServletRequest request) {
        log.warn("业务异常 [{}]: {}, 请求 URI: {}", e.getCode(), e.getMessage(), request.getRequestURI());
        return ApiResponse.fail(e.getCode(), e.getMessage());
    }

    /**
     * 参数校验异常处理 (由 @Valid 触发)
     * @param e 参数校验异常
     * @return ApiResponse
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST) // 400
    public ApiResponse<?> methodArgumentNotValidExceptionHandler(MethodArgumentNotValidException e, HttpServletRequest request) {
        BindingResult bindingResult = e.getBindingResult();
        Map<String, String> errors = new HashMap<>();
        for (FieldError fieldError : bindingResult.getFieldErrors()) {
            errors.put(fieldError.getField(), fieldError.getDefaultMessage());
        }
        log.warn("参数校验失败: {}, 请求 URI: {}", errors, request.getRequestURI());
        // 返回第一个校验失败的信息给前端，同时在 data 中附带所有错误详情
        String firstErrorMessage = bindingResult.getFieldErrors().get(0).getDefaultMessage();
        return ApiResponse.fail(ErrorCode.PARAMS_ERROR.getCode(), firstErrorMessage, errors);
    }

    /**
     * Spring Security 权限不足异常
     */
    @ExceptionHandler(AccessDeniedException.class)
    @ResponseStatus(HttpStatus.FORBIDDEN) // 403
    public ApiResponse<?> accessDeniedExceptionHandler(AccessDeniedException e, HttpServletRequest request) {
        log.warn("权限不足: {}, 请求 URI: {}", e.getMessage(), request.getRequestURI());
        return ApiResponse.fail(ErrorCode.FORBIDDEN_ERROR.getCode(), ErrorCode.FORBIDDEN_ERROR.getMessage());
    }

    /**
     * 请求方法不支持异常
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED) // 405
    public ApiResponse<?> methodNotSupportedExceptionHandler(HttpRequestMethodNotSupportedException e, HttpServletRequest request) {
        log.warn("请求方法不支持: {}, 请求 URI: {}", e.getMessage(), request.getRequestURI());
        return ApiResponse.fail(ErrorCode.METHOD_NOT_ALLOWED.getCode(), e.getMessage());
    }

    /**
     * Spring Security 认证异常处理
     */
    @ExceptionHandler({AuthenticationException.class, BadCredentialsException.class})
    @ResponseStatus(HttpStatus.UNAUTHORIZED) // 401
    public ApiResponse<?> authenticationExceptionHandler(Exception e, HttpServletRequest request) {
        log.warn("认证失败: {}, 请求 URI: {}", e.getMessage(), request.getRequestURI());
        return ApiResponse.fail(ErrorCode.NO_AUTH_ERROR.getCode(), "用户名或密码错误");
    }

    /**
     * 通用系统异常处理 (最终的兜底)
     * @param e 异常
     * @return ApiResponse
     */
    @ExceptionHandler(Exception.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) // 500
    public ApiResponse<?> exceptionHandler(Exception e, HttpServletRequest request) {
        log.error("系统异常, 请求 URI: " + request.getRequestURI(), e);
        return ApiResponse.fail(ErrorCode.SYSTEM_ERROR.getCode(), ErrorCode.SYSTEM_ERROR.getMessage());
    }
}